home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2000 March / maximum-cd-2000-03.iso / Quake3 Game Source / Q3AGameSource.exe / Main / g_cmds.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-01-18  |  25.7 KB  |  1,098 lines

  1. // Copyright (C) 1999-2000 Id Software, Inc.
  2. //
  3. #include "g_local.h"
  4.  
  5.  
  6. /*
  7. ==================
  8. DeathmatchScoreboardMessage
  9.  
  10. ==================
  11. */
  12. void DeathmatchScoreboardMessage( gentity_t *ent ) {
  13.     char        entry[1024];
  14.     char        string[1400];
  15.     int            stringlength;
  16.     int            i, j;
  17.     gclient_t    *cl;
  18.     int            numSorted;
  19.     int            scoreFlags;
  20.  
  21.     // send the latest information on all clients
  22.     string[0] = 0;
  23.     stringlength = 0;
  24.     scoreFlags = 0;
  25.  
  26.     // don't send more than 32 scores (FIXME?)
  27.     numSorted = level.numConnectedClients;
  28.     if ( numSorted > 32 ) {
  29.         numSorted = 32;
  30.     }
  31.  
  32.     for (i=0 ; i < numSorted ; i++) {
  33.         int        ping;
  34.  
  35.         cl = &level.clients[level.sortedClients[i]];
  36.  
  37.         if ( cl->pers.connected == CON_CONNECTING ) {
  38.             ping = -1;
  39.         } else {
  40.             ping = cl->ps.ping < 999 ? cl->ps.ping : 999;
  41.         }
  42.         Com_sprintf (entry, sizeof(entry),
  43.             " %i %i %i %i %i %i", level.sortedClients[i],
  44.             cl->ps.persistant[PERS_SCORE], ping, (level.time - cl->pers.enterTime)/60000,
  45.             scoreFlags, g_entities[level.sortedClients[i]].s.powerups);
  46.         j = strlen(entry);
  47.         if (stringlength + j > 1024)
  48.             break;
  49.         strcpy (string + stringlength, entry);
  50.         stringlength += j;
  51.     }
  52.  
  53.     trap_SendServerCommand( ent-g_entities, va("scores %i %i %i%s", i, 
  54.         level.teamScores[TEAM_RED], level.teamScores[TEAM_BLUE],
  55.         string ) );
  56. }
  57.  
  58.  
  59. /*
  60. ==================
  61. Cmd_Score_f
  62.  
  63. Request current scoreboard information
  64. ==================
  65. */
  66. void Cmd_Score_f( gentity_t *ent ) {
  67.     DeathmatchScoreboardMessage( ent );
  68. }
  69.  
  70.  
  71.  
  72. /*
  73. ==================
  74. CheatsOk
  75. ==================
  76. */
  77. qboolean    CheatsOk( gentity_t *ent ) {
  78.     if ( !g_cheats.integer ) {
  79.         trap_SendServerCommand( ent-g_entities, va("print \"Cheats are not enabled on this server.\n\""));
  80.         return qfalse;
  81.     }
  82.     if ( ent->health <= 0 ) {
  83.         trap_SendServerCommand( ent-g_entities, va("print \"You must be alive to use this command.\n\""));
  84.         return qfalse;
  85.     }
  86.     return qtrue;
  87. }
  88.  
  89.  
  90. /*
  91. ==================
  92. ConcatArgs
  93. ==================
  94. */
  95. char    *ConcatArgs( int start ) {
  96.     int        i, c, tlen;
  97.     static char    line[MAX_STRING_CHARS];
  98.     int        len;
  99.     char    arg[MAX_STRING_CHARS];
  100.  
  101.     len = 0;
  102.     c = trap_Argc();
  103.     for ( i = start ; i < c ; i++ ) {
  104.         trap_Argv( i, arg, sizeof( arg ) );
  105.         tlen = strlen( arg );
  106.         if ( len + tlen >= MAX_STRING_CHARS - 1 ) {
  107.             break;
  108.         }
  109.         memcpy( line + len, arg, tlen );
  110.         len += tlen;
  111.         if ( i != c - 1 ) {
  112.             line[len] = ' ';
  113.             len++;
  114.         }
  115.     }
  116.  
  117.     line[len] = 0;
  118.  
  119.     return line;
  120. }
  121.  
  122. /*
  123. ==================
  124. SanitizeString
  125.  
  126. Remove case and control characters
  127. ==================
  128. */
  129. void SanitizeString( char *in, char *out ) {
  130.     while ( *in ) {
  131.         if ( *in == 27 ) {
  132.             in += 2;        // skip color code
  133.             continue;
  134.         }
  135.         if ( *in < 32 ) {
  136.             in++;
  137.             continue;
  138.         }
  139.         *out++ = tolower( *in++ );
  140.     }
  141.  
  142.     *out = 0;
  143. }
  144.  
  145. /*
  146. ==================
  147. ClientNumberFromString
  148.  
  149. Returns a player number for either a number or name string
  150. Returns -1 if invalid
  151. ==================
  152. */
  153. int ClientNumberFromString( gentity_t *to, char *s ) {
  154.     gclient_t    *cl;
  155.     int            idnum;
  156.     char        s2[MAX_STRING_CHARS];
  157.     char        n2[MAX_STRING_CHARS];
  158.  
  159.     // numeric values are just slot numbers
  160.     if (s[0] >= '0' && s[0] <= '9') {
  161.         idnum = atoi( s );
  162.         if ( idnum < 0 || idnum >= level.maxclients ) {
  163.             trap_SendServerCommand( to-g_entities, va("print \"Bad client slot: %i\n\"", idnum));
  164.             return -1;
  165.         }
  166.  
  167.         cl = &level.clients[idnum];
  168.         if ( cl->pers.connected != CON_CONNECTED ) {
  169.             trap_SendServerCommand( to-g_entities, va("print \"Client %i is not active\n\"", idnum));
  170.             return -1;
  171.         }
  172.         return idnum;
  173.     }
  174.  
  175.     // check for a name match
  176.     SanitizeString( s, s2 );
  177.     for ( idnum=0,cl=level.clients ; idnum < level.maxclients ; idnum++,cl++ ) {
  178.         if ( cl->pers.connected != CON_CONNECTED ) {
  179.             continue;
  180.         }
  181.         SanitizeString( cl->pers.netname, n2 );
  182.         if ( !strcmp( n2, s2 ) ) {
  183.             return idnum;
  184.         }
  185.     }
  186.  
  187.     trap_SendServerCommand( to-g_entities, va("print \"User %s is not on the server\n\"", s));
  188.     return -1;
  189. }
  190.  
  191. /*
  192. ==================
  193. Cmd_Give_f
  194.  
  195. Give items to a client
  196. ==================
  197. */
  198. void Cmd_Give_f (gentity_t *ent)
  199. {
  200.     char        *name;
  201.     gitem_t        *it;
  202.     int            i;
  203.     qboolean    give_all;
  204.     gentity_t        *it_ent;
  205.     trace_t        trace;
  206.  
  207.     if ( !CheatsOk( ent ) ) {
  208.         return;
  209.     }
  210.  
  211.     name = ConcatArgs( 1 );
  212.  
  213.     if (Q_stricmp(name, "all") == 0)
  214.         give_all = qtrue;
  215.     else
  216.         give_all = qfalse;
  217.  
  218.     if (give_all || Q_stricmp( name, "health") == 0)
  219.     {
  220.         ent->health = ent->client->ps.stats[STAT_MAX_HEALTH];
  221.         if (!give_all)
  222.             return;
  223.     }
  224.  
  225.     if (give_all || Q_stricmp(name, "weapons") == 0)
  226.     {
  227.         ent->client->ps.stats[STAT_WEAPONS] = (1 << WP_NUM_WEAPONS) - 1 - 
  228.             ( 1 << WP_GRAPPLING_HOOK ) - ( 1 << WP_NONE );
  229.         if (!give_all)
  230.             return;
  231.     }
  232.  
  233.     if (give_all || Q_stricmp(name, "ammo") == 0)
  234.     {
  235.         for ( i = 0 ; i < MAX_WEAPONS ; i++ ) {
  236.             ent->client->ps.ammo[i] = 999;
  237.         }
  238.         if (!give_all)
  239.             return;
  240.     }
  241.  
  242.     if (give_all || Q_stricmp(name, "armor") == 0)
  243.     {
  244.         ent->client->ps.stats[STAT_ARMOR] = 200;
  245.  
  246.         if (!give_all)
  247.             return;
  248.     }
  249.  
  250.     // spawn a specific item right on the player
  251.     if ( !give_all ) {
  252.         it = BG_FindItem (name);
  253.         if (!it) {
  254.             return;
  255.         }
  256.  
  257.         it_ent = G_Spawn();
  258.         VectorCopy( ent->r.currentOrigin, it_ent->s.origin );
  259.         it_ent->classname = it->classname;
  260.         G_SpawnItem (it_ent, it);
  261.         FinishSpawningItem(it_ent );
  262.         memset( &trace, 0, sizeof( trace ) );
  263.         Touch_Item (it_ent, ent, &trace);
  264.         if (it_ent->inuse) {
  265.             G_FreeEntity( it_ent );
  266.         }
  267.     }
  268. }
  269.  
  270.  
  271. /*
  272. ==================
  273. Cmd_God_f
  274.  
  275. Sets client to godmode
  276.  
  277. argv(0) god
  278. ==================
  279. */
  280. void Cmd_God_f (gentity_t *ent)
  281. {
  282.     char    *msg;
  283.  
  284.     if ( !CheatsOk( ent ) ) {
  285.         return;
  286.     }
  287.  
  288.     ent->flags ^= FL_GODMODE;
  289.     if (!(ent->flags & FL_GODMODE) )
  290.         msg = "godmode OFF\n";
  291.     else
  292.         msg = "godmode ON\n";
  293.  
  294.     trap_SendServerCommand( ent-g_entities, va("print \"%s\"", msg));
  295. }
  296.  
  297.  
  298. /*
  299. ==================
  300. Cmd_Notarget_f
  301.  
  302. Sets client to notarget
  303.  
  304. argv(0) notarget
  305. ==================
  306. */
  307. void Cmd_Notarget_f( gentity_t *ent ) {
  308.     char    *msg;
  309.  
  310.     if ( !CheatsOk( ent ) ) {
  311.         return;
  312.     }
  313.  
  314.     ent->flags ^= FL_NOTARGET;
  315.     if (!(ent->flags & FL_NOTARGET) )
  316.         msg = "notarget OFF\n";
  317.     else
  318.         msg = "notarget ON\n";
  319.  
  320.     trap_SendServerCommand( ent-g_entities, va("print \"%s\"", msg));
  321. }
  322.  
  323.  
  324. /*
  325. ==================
  326. Cmd_Noclip_f
  327.  
  328. argv(0) noclip
  329. ==================
  330. */
  331. void Cmd_Noclip_f( gentity_t *ent ) {
  332.     char    *msg;
  333.  
  334.     if ( !CheatsOk( ent ) ) {
  335.         return;
  336.     }
  337.  
  338.     if ( ent->client->noclip ) {
  339.         msg = "noclip OFF\n";
  340.     } else {
  341.         msg = "noclip ON\n";
  342.     }
  343.     ent->client->noclip = !ent->client->noclip;
  344.  
  345.     trap_SendServerCommand( ent-g_entities, va("print \"%s\"", msg));
  346. }
  347.  
  348.  
  349. /*
  350. ==================
  351. Cmd_LevelShot_f
  352.  
  353. This is just to help generate the level pictures
  354. for the menus.  It goes to the intermission immediately
  355. and sends over a command to the client to resize the view,
  356. hide the scoreboard, and take a special screenshot
  357. ==================
  358. */
  359. void Cmd_LevelShot_f( gentity_t *ent ) {
  360.     if ( !CheatsOk( ent ) ) {
  361.         return;
  362.     }
  363.  
  364.     // doesn't work in single player
  365.     if ( g_gametype.integer != 0 ) {
  366.         trap_SendServerCommand( ent-g_entities, 
  367.             "print \"Must be in g_gametype 0 for levelshot\n\"" );
  368.         return;
  369.     }
  370.  
  371.     BeginIntermission();
  372.     trap_SendServerCommand( ent-g_entities, "clientLevelShot" );
  373. }
  374.  
  375.  
  376. /*
  377. =================
  378. Cmd_Kill_f
  379. =================
  380. */
  381. void Cmd_Kill_f( gentity_t *ent ) {
  382.     if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
  383.         return;
  384.     }
  385.     ent->flags &= ~FL_GODMODE;
  386.     ent->client->ps.stats[STAT_HEALTH] = ent->health = 0;
  387.     player_die (ent, ent, ent, 100000, MOD_SUICIDE);
  388. }
  389.  
  390. /*
  391. =================
  392. BroadCastTeamChange
  393.  
  394. Let everyone know about a team change
  395. =================
  396. */
  397. void BroadcastTeamChange( gclient_t *client, int oldTeam )
  398. {
  399.     if ( client->sess.sessionTeam == TEAM_RED ) {
  400.         trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined the red team.\n\"",
  401.             client->pers.netname) );
  402.     } else if ( client->sess.sessionTeam == TEAM_BLUE ) {
  403.         trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined the blue team.\n\"",
  404.         client->pers.netname));
  405.     } else if ( client->sess.sessionTeam == TEAM_SPECTATOR && oldTeam != TEAM_SPECTATOR ) {
  406.         trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined the spectators.\n\"",
  407.         client->pers.netname));
  408.     } else if ( client->sess.sessionTeam == TEAM_FREE ) {
  409.         trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined the battle.\n\"",
  410.         client->pers.netname));
  411.     }
  412. }
  413.  
  414. /*
  415. =================
  416. SetTeam
  417. =================
  418. */
  419. void SetTeam( gentity_t *ent, char *s ) {
  420.     int                    team, oldTeam;
  421.     gclient_t            *client;
  422.     int                    clientNum;
  423.     spectatorState_t    specState;
  424.     int                    specClient;
  425.  
  426.     //
  427.     // see what change is requested
  428.     //
  429.     client = ent->client;
  430.  
  431.     clientNum = client - level.clients;
  432.     specClient = 0;
  433.  
  434.     specState = SPECTATOR_NOT;
  435.     if ( !Q_stricmp( s, "scoreboard" ) || !Q_stricmp( s, "score" )  ) {
  436.         team = TEAM_SPECTATOR;
  437.         specState = SPECTATOR_SCOREBOARD;
  438.     } else if ( !Q_stricmp( s, "follow1" ) ) {
  439.         team = TEAM_SPECTATOR;
  440.         specState = SPECTATOR_FOLLOW;
  441.         specClient = -1;
  442.     } else if ( !Q_stricmp( s, "follow2" ) ) {
  443.         team = TEAM_SPECTATOR;
  444.         specState = SPECTATOR_FOLLOW;
  445.         specClient = -2;
  446.     } else if ( !Q_stricmp( s, "spectator" ) || !Q_stricmp( s, "s" ) ) {
  447.         team = TEAM_SPECTATOR;
  448.         specState = SPECTATOR_FREE;
  449.     } else if ( g_gametype.integer >= GT_TEAM ) {
  450.         // if running a team game, assign player to one of the teams
  451.         specState = SPECTATOR_NOT;
  452.         if ( !Q_stricmp( s, "red" ) || !Q_stricmp( s, "r" ) ) {
  453.             team = TEAM_RED;
  454.         } else if ( !Q_stricmp( s, "blue" ) || !Q_stricmp( s, "b" ) ) {
  455.             team = TEAM_BLUE;
  456.         } else {
  457.             // pick the team with the least number of players
  458.             team = PickTeam( clientNum );
  459.         }
  460.  
  461.         if ( g_teamForceBalance.integer ) {
  462.             int        counts[TEAM_NUM_TEAMS];
  463.  
  464.             counts[TEAM_BLUE] = TeamCount( ent->client->ps.clientNum, TEAM_BLUE );
  465.             counts[TEAM_RED] = TeamCount( ent->client->ps.clientNum, TEAM_RED );
  466.  
  467.             // We allow a spread of two
  468.             if ( team == TEAM_RED && counts[TEAM_RED] - counts[TEAM_BLUE] > 1 ) {
  469.                 trap_SendServerCommand( ent->client->ps.clientNum, 
  470.                     "cp \"Red team has too many players.\n\"" );
  471.                 return; // ignore the request
  472.             }
  473.             if ( team == TEAM_BLUE && counts[TEAM_BLUE] - counts[TEAM_RED] > 1 ) {
  474.                 trap_SendServerCommand( ent->client->ps.clientNum, 
  475.                     "cp \"Red team has too many players.\n\"" );
  476.                 return; // ignore the request
  477.             }
  478.  
  479.             // It's ok, the team we are switching to has less or same number of players
  480.         }
  481.  
  482.     } else {
  483.         // force them to spectators if there aren't any spots free
  484.         team = TEAM_FREE;
  485.     }
  486.  
  487.     // override decision if limiting the players
  488.     if ( g_gametype.integer == GT_TOURNAMENT
  489.         && level.numNonSpectatorClients >= 2 ) {
  490.         team = TEAM_SPECTATOR;
  491.     } else if ( g_maxGameClients.integer > 0 && 
  492.         level.numNonSpectatorClients >= g_maxGameClients.integer ) {
  493.         team = TEAM_SPECTATOR;
  494.     }
  495.  
  496.     //
  497.     // decide if we will allow the change
  498.     //
  499.     oldTeam = client->sess.sessionTeam;
  500.     if ( team == oldTeam && team != TEAM_SPECTATOR ) {
  501.         return;
  502.     }
  503.  
  504.     //
  505.     // execute the team change
  506.     //
  507.  
  508.     // he starts at 'base'
  509.     client->pers.teamState.state = TEAM_BEGIN;
  510.     if ( oldTeam != TEAM_SPECTATOR ) {
  511.         // Kill him (makes sure he loses flags, etc)
  512.         ent->flags &= ~FL_GODMODE;
  513.         ent->client->ps.stats[STAT_HEALTH] = ent->health = 0;
  514.         player_die (ent, ent, ent, 100000, MOD_SUICIDE);
  515.  
  516.     }
  517.     // they go to the end of the line for tournements
  518.     if ( team == TEAM_SPECTATOR ) {
  519.         client->sess.spectatorTime = level.time;
  520.     }
  521.  
  522.     client->sess.sessionTeam = team;
  523.     client->sess.spectatorState = specState;
  524.     client->sess.spectatorClient = specClient;
  525.  
  526.     BroadcastTeamChange( client, oldTeam );
  527.  
  528.     // get and distribute relevent paramters
  529.     ClientUserinfoChanged( clientNum );
  530.  
  531.     ClientBegin( clientNum );
  532. }
  533.  
  534. /*
  535. =================
  536. StopFollowing
  537.  
  538. If the client being followed leaves the game, or you just want to drop
  539. to free floating spectator mode
  540. =================
  541. */
  542. void StopFollowing( gentity_t *ent ) {
  543.     ent->client->ps.persistant[ PERS_TEAM ] = TEAM_SPECTATOR;    
  544.     ent->client->sess.sessionTeam = TEAM_SPECTATOR;    
  545.     ent->client->sess.spectatorState = SPECTATOR_FREE;
  546.     ent->r.svFlags &= ~SVF_BOT;
  547.     ent->client->ps.clientNum = ent - g_entities;
  548. }
  549.  
  550. /*
  551. =================
  552. Cmd_Team_f
  553. =================
  554. */
  555. void Cmd_Team_f( gentity_t *ent ) {
  556.     int            oldTeam;
  557.     char        s[MAX_TOKEN_CHARS];
  558.  
  559.     if ( trap_Argc() != 2 ) {
  560.         oldTeam = ent->client->sess.sessionTeam;
  561.         switch ( oldTeam ) {
  562.         case TEAM_BLUE:
  563.             trap_SendServerCommand( ent-g_entities, "print \"Blue team\n\"" );
  564.             break;
  565.         case TEAM_RED:
  566.             trap_SendServerCommand( ent-g_entities, "print \"Red team\n\"" );
  567.             break;
  568.         case TEAM_FREE:
  569.             trap_SendServerCommand( ent-g_entities, "print \"Free team\n\"" );
  570.             break;
  571.         case TEAM_SPECTATOR:
  572.             trap_SendServerCommand( ent-g_entities, "print \"Spectator team\n\"" );
  573.             break;
  574.         }
  575.         return;
  576.     }
  577.  
  578.     // if they are playing a tournement game, count as a loss
  579.     if ( g_gametype.integer == GT_TOURNAMENT && ent->client->sess.sessionTeam == TEAM_FREE ) {
  580.         ent->client->sess.losses++;
  581.     }
  582.  
  583.     trap_Argv( 1, s, sizeof( s ) );
  584.  
  585.     SetTeam( ent, s );
  586. }
  587.  
  588.  
  589. /*
  590. =================
  591. Cmd_Follow_f
  592. =================
  593. */
  594. void Cmd_Follow_f( gentity_t *ent ) {
  595.     int        i;
  596.     char    arg[MAX_TOKEN_CHARS];
  597.  
  598.     if ( trap_Argc() != 2 ) {
  599.         if ( ent->client->sess.spectatorState == SPECTATOR_FOLLOW ) {
  600.             StopFollowing( ent );
  601.         }
  602.         return;
  603.     }
  604.  
  605.     trap_Argv( 1, arg, sizeof( arg ) );
  606.     i = ClientNumberFromString( ent, arg );
  607.     if ( i == -1 ) {
  608.         return;
  609.     }
  610.  
  611.     // can't follow self
  612.     if ( &level.clients[ i ] == ent->client ) {
  613.         return;
  614.     }
  615.  
  616.     // can't follow another spectator
  617.     if ( level.clients[ i ].sess.sessionTeam == TEAM_SPECTATOR ) {
  618.         return;
  619.     }
  620.  
  621.     // if they are playing a tournement game, count as a loss
  622.     if ( g_gametype.integer == GT_TOURNAMENT && ent->client->sess.sessionTeam == TEAM_FREE ) {
  623.         ent->client->sess.losses++;
  624.     }
  625.  
  626.     // first set them to spectator
  627.     if ( ent->client->sess.sessionTeam != TEAM_SPECTATOR ) {
  628.         SetTeam( ent, "spectator" );
  629.     }
  630.  
  631.     ent->client->sess.spectatorState = SPECTATOR_FOLLOW;
  632.     ent->client->sess.spectatorClient = i;
  633. }
  634.  
  635. /*
  636. =================
  637. Cmd_FollowCycle_f
  638. =================
  639. */
  640. void Cmd_FollowCycle_f( gentity_t *ent, int dir ) {
  641.     int        clientnum;
  642.     int        original;
  643.  
  644.     // if they are playing a tournement game, count as a loss
  645.     if ( g_gametype.integer == GT_TOURNAMENT && ent->client->sess.sessionTeam == TEAM_FREE ) {
  646.         ent->client->sess.losses++;
  647.     }
  648.     // first set them to spectator
  649.     if ( ent->client->sess.spectatorState == SPECTATOR_NOT ) {
  650.         SetTeam( ent, "spectator" );
  651.     }
  652.  
  653.     if ( dir != 1 && dir != -1 ) {
  654.         G_Error( "Cmd_FollowCycle_f: bad dir %i", dir );
  655.     }
  656.  
  657.     clientnum = ent->client->sess.spectatorClient;
  658.     original = clientnum;
  659.     do {
  660.         clientnum += dir;
  661.         if ( clientnum >= level.maxclients ) {
  662.             clientnum = 0;
  663.         }
  664.         if ( clientnum < 0 ) {
  665.             clientnum = level.maxclients - 1;
  666.         }
  667.  
  668.         // can only follow connected clients
  669.         if ( level.clients[ clientnum ].pers.connected != CON_CONNECTED ) {
  670.             continue;
  671.         }
  672.  
  673.         // can't follow another spectator
  674.         if ( level.clients[ clientnum ].sess.sessionTeam == TEAM_SPECTATOR ) {
  675.             continue;
  676.         }
  677.  
  678.         // this is good, we can use it
  679.         ent->client->sess.spectatorClient = clientnum;
  680.         ent->client->sess.spectatorState = SPECTATOR_FOLLOW;
  681.         return;
  682.     } while ( clientnum != original );
  683.  
  684.     // leave it where it was
  685. }
  686.  
  687.  
  688. /*
  689. ==================
  690. G_Say
  691. ==================
  692. */
  693. #define    MAX_SAY_TEXT    150
  694.  
  695. #define SAY_ALL        0
  696. #define SAY_TEAM    1
  697. #define SAY_TELL    2
  698.  
  699. static void G_SayTo( gentity_t *ent, gentity_t *other, int mode, int color, const char *name, const char *message ) {
  700.     if (!other) {
  701.         return;
  702.     }
  703.     if (!other->inuse) {
  704.         return;
  705.     }
  706.     if (!other->client) {
  707.         return;
  708.     }
  709.     if ( mode == SAY_TEAM  && !OnSameTeam(ent, other) ) {
  710.         return;
  711.     }
  712.     // no chatting to players in tournements
  713.     if ( g_gametype.integer == GT_TOURNAMENT
  714.         && other->client->sess.sessionTeam == TEAM_FREE
  715.         && ent->client->sess.sessionTeam != TEAM_FREE ) {
  716.         return;
  717.     }
  718.  
  719.     trap_SendServerCommand( other-g_entities, va("%s \"%s%c%c%s\"", 
  720.         mode == SAY_TEAM ? "tchat" : "chat",
  721.         name, Q_COLOR_ESCAPE, color, message));
  722. }
  723.  
  724. void G_Say( gentity_t *ent, gentity_t *target, int mode, const char *chatText ) {
  725.     int            j;
  726.     gentity_t    *other;
  727.     int            color;
  728.     char        name[64];
  729.     // don't let text be too long for malicious reasons
  730.     char        text[MAX_SAY_TEXT];
  731.     char        location[64];
  732.  
  733.     if ( g_gametype.integer < GT_TEAM && mode == SAY_TEAM ) {
  734.         mode = SAY_ALL;
  735.     }
  736.  
  737.     switch ( mode ) {
  738.     default:
  739.     case SAY_ALL:
  740.         G_LogPrintf( "say: %s: %s\n", ent->client->pers.netname, chatText );
  741.         Com_sprintf (name, sizeof(name), "%s%c%c: ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE );
  742.         color = COLOR_GREEN;
  743.         break;
  744.     case SAY_TEAM:
  745.         G_LogPrintf( "sayteam: %s: %s\n", ent->client->pers.netname, chatText );
  746.         if (Team_GetLocationMsg(ent, location, sizeof(location)))
  747.             Com_sprintf (name, sizeof(name), "(%s%c%c) (%s): ", 
  748.                 ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE, location);
  749.         else
  750.             Com_sprintf (name, sizeof(name), "(%s%c%c): ", 
  751.                 ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE );
  752.         color = COLOR_CYAN;
  753.         break;
  754.     case SAY_TELL:
  755.         if (target && g_gametype.integer >= GT_TEAM &&
  756.             target->client->sess.sessionTeam == ent->client->sess.sessionTeam &&
  757.             Team_GetLocationMsg(ent, location, sizeof(location)))
  758.             Com_sprintf (name, sizeof(name), "[%s%c%c] (%s): ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE, location );
  759.         else
  760.             Com_sprintf (name, sizeof(name), "[%s%c%c]: ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE );
  761.         color = COLOR_MAGENTA;
  762.         break;
  763.     }
  764.  
  765.     Q_strncpyz( text, chatText, sizeof(text) );
  766.  
  767.     if ( target ) {
  768.         G_SayTo( ent, target, mode, color, name, text );
  769.         return;
  770.     }
  771.  
  772.     // echo the text to the console
  773.     if ( g_dedicated.integer ) {
  774.         G_Printf( "%s%s\n", name, text);
  775.     }
  776.  
  777.     // send it to all the apropriate clients
  778.     for (j = 0; j < level.maxclients; j++) {
  779.         other = &g_entities[j];
  780.         G_SayTo( ent, other, mode, color, name, text );
  781.     }
  782. }
  783.  
  784.  
  785. /*
  786. ==================
  787. Cmd_Say_f
  788. ==================
  789. */
  790. static void Cmd_Say_f( gentity_t *ent, int mode, qboolean arg0 ) {
  791.     char        *p;
  792.  
  793.     if ( trap_Argc () < 2 && !arg0 ) {
  794.         return;
  795.     }
  796.  
  797.     if (arg0)
  798.     {
  799.         p = ConcatArgs( 0 );
  800.     }
  801.     else
  802.     {
  803.         p = ConcatArgs( 1 );
  804.     }
  805.  
  806.     G_Say( ent, NULL, mode, p );
  807. }
  808.  
  809. /*
  810. ==================
  811. Cmd_Tell_f
  812. ==================
  813. */
  814. static void Cmd_Tell_f( gentity_t *ent ) {
  815.     int            targetNum;
  816.     gentity_t    *target;
  817.     char        *p;
  818.     char        arg[MAX_TOKEN_CHARS];
  819.  
  820.     if ( trap_Argc () < 2 ) {
  821.         return;
  822.     }
  823.  
  824.     trap_Argv( 1, arg, sizeof( arg ) );
  825.     targetNum = atoi( arg );
  826.     if ( targetNum < 0 || targetNum >= level.maxclients ) {
  827.         return;
  828.     }
  829.  
  830.     target = &g_entities[targetNum];
  831.     if ( !target || !target->inuse || !target->client ) {
  832.         return;
  833.     }
  834.  
  835.     p = ConcatArgs( 2 );
  836.  
  837.     G_LogPrintf( "tell: %s to %s: %s\n", ent->client->pers.netname, target->client->pers.netname, p );
  838.     G_Say( ent, target, SAY_TELL, p );
  839.     G_Say( ent, ent, SAY_TELL, p );
  840. }
  841.  
  842.  
  843. static char    *gc_orders[] = {
  844.     "hold your position",
  845.     "hold this position",
  846.     "come here",
  847.     "cover me",
  848.     "guard location",
  849.     "search and destroy",
  850.     "report"
  851. };
  852.  
  853. void Cmd_GameCommand_f( gentity_t *ent ) {
  854.     int        player;
  855.     int        order;
  856.     char    str[MAX_TOKEN_CHARS];
  857.  
  858.     trap_Argv( 1, str, sizeof( str ) );
  859.     player = atoi( str );
  860.     trap_Argv( 2, str, sizeof( str ) );
  861.     order = atoi( str );
  862.  
  863.     if ( player < 0 || player >= MAX_CLIENTS ) {
  864.         return;
  865.     }
  866.     if ( order < 0 || order > sizeof(gc_orders)/sizeof(char *) ) {
  867.         return;
  868.     }
  869.     G_Say( ent, &g_entities[player], SAY_TELL, gc_orders[order] );
  870.     G_Say( ent, ent, SAY_TELL, gc_orders[order] );
  871. }
  872.  
  873. /*
  874. ==================
  875. Cmd_Where_f
  876. ==================
  877. */
  878. void Cmd_Where_f( gentity_t *ent ) {
  879.     trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", vtos( ent->s.origin ) ) );
  880. }
  881.  
  882.  
  883. /*
  884. ==================
  885. Cmd_CallVote_f
  886. ==================
  887. */
  888. void Cmd_CallVote_f( gentity_t *ent ) {
  889.     int        i;
  890.     char    arg1[MAX_STRING_TOKENS];
  891.     char    arg2[MAX_STRING_TOKENS];
  892.  
  893.     if ( !g_allowVote.integer ) {
  894.         trap_SendServerCommand( ent-g_entities, "print \"Voting not allowed here.\n\"" );
  895.         return;
  896.     }
  897.  
  898.     if ( level.voteTime ) {
  899.         trap_SendServerCommand( ent-g_entities, "print \"A vote is already in progress.\n\"" );
  900.         return;
  901.     }
  902.     if ( ent->client->pers.voteCount >= MAX_VOTE_COUNT ) {
  903.         trap_SendServerCommand( ent-g_entities, "print \"You have called the maximum number of votes.\n\"" );
  904.         return;
  905.     }
  906.  
  907.     // make sure it is a valid command to vote on
  908.     trap_Argv( 1, arg1, sizeof( arg1 ) );
  909.     trap_Argv( 2, arg2, sizeof( arg2 ) );
  910.  
  911.     if ( !Q_stricmp( arg1, "map_restart" ) ) {
  912.     } else if ( !Q_stricmp( arg1, "nextmap" ) ) {
  913.     } else if ( !Q_stricmp( arg1, "map" ) ) {
  914.     } else if ( !Q_stricmp( arg1, "g_gametype" ) ) {
  915.     } else if ( !Q_stricmp( arg1, "kick" ) ) {
  916.     } else if ( !Q_stricmp( arg1, "g_doWarmup" ) ) {
  917.     } else {
  918.         trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string.\n\"" );
  919.         trap_SendServerCommand( ent-g_entities, "print \"Vote commands are: map_restart, nextmap, map <mapname>, g_gametype <n> and kick <player>.\n\"" );
  920.         return;
  921.     }
  922.  
  923.     if ( !Q_stricmp( arg1, "map" ) ) {
  924.         // special case for map changes, we want to reset the nextmap setting
  925.         // this allows a player to change maps, but not upset the map rotation
  926.         char    s[MAX_STRING_CHARS];
  927.  
  928.         trap_Cvar_VariableStringBuffer( "nextmap", s, sizeof(s) );
  929.         if (*s) {
  930.             Com_sprintf( level.voteString, sizeof( level.voteString ), "%s %s; set nextmap \"%s\"", arg1, arg2, s );
  931.         } else {
  932.             Com_sprintf( level.voteString, sizeof( level.voteString ), "%s %s", arg1, arg2 );
  933.         }
  934.  
  935.     } else {
  936.         Com_sprintf( level.voteString, sizeof( level.voteString ), "%s %s", arg1, arg2 );
  937.     }
  938.  
  939.     trap_SendServerCommand( -1, va("print \"%s called a vote.\n\"", ent->client->pers.netname ) );
  940.  
  941.     // start the voting, the caller autoamtically votes yes
  942.     level.voteTime = level.time;
  943.     level.voteYes = 1;
  944.     level.voteNo = 0;
  945.  
  946.     for ( i = 0 ; i < level.maxclients ; i++ ) {
  947.         level.clients[i].ps.eFlags &= ~EF_VOTED;
  948.     }
  949.     ent->client->ps.eFlags |= EF_VOTED;
  950.  
  951.     trap_SetConfigstring( CS_VOTE_TIME, va("%i", level.voteTime ) );
  952.     trap_SetConfigstring( CS_VOTE_STRING, level.voteString );    
  953.     trap_SetConfigstring( CS_VOTE_YES, va("%i", level.voteYes ) );
  954.     trap_SetConfigstring( CS_VOTE_NO, va("%i", level.voteNo ) );    
  955. }
  956.  
  957. /*
  958. ==================
  959. Cmd_Vote_f
  960. ==================
  961. */
  962. void Cmd_Vote_f( gentity_t *ent ) {
  963.     char        msg[64];
  964.  
  965.     if ( !level.voteTime ) {
  966.         trap_SendServerCommand( ent-g_entities, "print \"No vote in progress.\n\"" );
  967.         return;
  968.     }
  969.     if ( ent->client->ps.eFlags & EF_VOTED ) {
  970.         trap_SendServerCommand( ent-g_entities, "print \"Vote already cast.\n\"" );
  971.         return;
  972.     }
  973.  
  974.     trap_SendServerCommand( ent-g_entities, "print \"Vote cast.\n\"" );
  975.  
  976.     ent->client->ps.eFlags |= EF_VOTED;
  977.  
  978.     trap_Argv( 1, msg, sizeof( msg ) );
  979.  
  980.     if ( msg[0] == 'y' || msg[1] == 'Y' || msg[1] == '1' ) {
  981.         level.voteYes++;
  982.         trap_SetConfigstring( CS_VOTE_YES, va("%i", level.voteYes ) );
  983.     } else {
  984.         level.voteNo++;
  985.         trap_SetConfigstring( CS_VOTE_NO, va("%i", level.voteNo ) );    
  986.     }
  987.  
  988.     // a majority will be determined in G_CheckVote, which will also account
  989.     // for players entering or leaving
  990. }
  991.  
  992.  
  993. /*
  994. =================
  995. Cmd_SetViewpos_f
  996. =================
  997. */
  998. void Cmd_SetViewpos_f( gentity_t *ent ) {
  999.     vec3_t        origin, angles;
  1000.     char        buffer[MAX_TOKEN_CHARS];
  1001.     int            i;
  1002.  
  1003.     if ( !g_cheats.integer ) {
  1004.         trap_SendServerCommand( ent-g_entities, va("print \"Cheats are not enabled on this server.\n\""));
  1005.         return;
  1006.     }
  1007.     if ( trap_Argc() != 5 ) {
  1008.         trap_SendServerCommand( ent-g_entities, va("print \"usage: setviewpos x y z yaw\n\""));
  1009.         return;
  1010.     }
  1011.  
  1012.     VectorClear( angles );
  1013.     for ( i = 0 ; i < 3 ; i++ ) {
  1014.         trap_Argv( i + 1, buffer, sizeof( buffer ) );
  1015.         origin[i] = atof( buffer );
  1016.     }
  1017.  
  1018.     trap_Argv( 4, buffer, sizeof( buffer ) );
  1019.     angles[YAW] = atof( buffer );
  1020.  
  1021.     TeleportPlayer( ent, origin, angles );
  1022. }
  1023.  
  1024.  
  1025. /*
  1026. =================
  1027. ClientCommand
  1028. =================
  1029. */
  1030. void ClientCommand( int clientNum ) {
  1031.     gentity_t *ent;
  1032.     char    cmd[MAX_TOKEN_CHARS];
  1033.  
  1034.     ent = g_entities + clientNum;
  1035.     if ( !ent->client ) {
  1036.         return;        // not fully in game yet
  1037.     }
  1038.  
  1039.  
  1040.     trap_Argv( 0, cmd, sizeof( cmd ) );
  1041.  
  1042.     if (Q_stricmp (cmd, "say") == 0) {
  1043.         Cmd_Say_f (ent, SAY_ALL, qfalse);
  1044.         return;
  1045.     }
  1046.     if (Q_stricmp (cmd, "say_team") == 0) {
  1047.         Cmd_Say_f (ent, SAY_TEAM, qfalse);
  1048.         return;
  1049.     }
  1050.     if (Q_stricmp (cmd, "tell") == 0) {
  1051.         Cmd_Tell_f ( ent );
  1052.         return;
  1053.     }
  1054.     if (Q_stricmp (cmd, "score") == 0) {
  1055.         Cmd_Score_f (ent);
  1056.         return;
  1057.     }
  1058.  
  1059.     // ignore all other commands when at intermission
  1060.     if (level.intermissiontime) {
  1061.         Cmd_Say_f (ent, qfalse, qtrue);
  1062.         return;
  1063.     }
  1064.  
  1065.     if (Q_stricmp (cmd, "give") == 0)
  1066.         Cmd_Give_f (ent);
  1067.     else if (Q_stricmp (cmd, "god") == 0)
  1068.         Cmd_God_f (ent);
  1069.     else if (Q_stricmp (cmd, "notarget") == 0)
  1070.         Cmd_Notarget_f (ent);
  1071.     else if (Q_stricmp (cmd, "noclip") == 0)
  1072.         Cmd_Noclip_f (ent);
  1073.     else if (Q_stricmp (cmd, "kill") == 0)
  1074.         Cmd_Kill_f (ent);
  1075.     else if (Q_stricmp (cmd, "levelshot") == 0)
  1076.         Cmd_LevelShot_f (ent);
  1077.     else if (Q_stricmp (cmd, "follow") == 0)
  1078.         Cmd_Follow_f (ent);
  1079.     else if (Q_stricmp (cmd, "follownext") == 0)
  1080.         Cmd_FollowCycle_f (ent, 1);
  1081.     else if (Q_stricmp (cmd, "followprev") == 0)
  1082.         Cmd_FollowCycle_f (ent, -1);
  1083.     else if (Q_stricmp (cmd, "team") == 0)
  1084.         Cmd_Team_f (ent);
  1085.     else if (Q_stricmp (cmd, "where") == 0)
  1086.         Cmd_Where_f (ent);
  1087.     else if (Q_stricmp (cmd, "callvote") == 0)
  1088.         Cmd_CallVote_f (ent);
  1089.     else if (Q_stricmp (cmd, "vote") == 0)
  1090.         Cmd_Vote_f (ent);
  1091.     else if (Q_stricmp (cmd, "gc") == 0)
  1092.         Cmd_GameCommand_f( ent );
  1093.     else if (Q_stricmp (cmd, "setviewpos") == 0)
  1094.         Cmd_SetViewpos_f( ent );
  1095.     else
  1096.         trap_SendServerCommand( clientNum, va("print \"unknown cmd %s\n\"", cmd ) );
  1097. }
  1098.